package fr.cephb.berkeley;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import com.sleepycat.db.DatabaseEntry;
import com.sleepycat.db.DatabaseException;

import fr.cephb.lang.Endian;
import fr.cephb.lang.UInt32;
/**
 * An utility class writing simple types (int8,int16...) to an internal stream of bytes and converting it into an array of byte 
 * or to a  com.sleepycat.db.DatabaseEntry
 * <b>Warning</b>: this class is <b>INDIAN sensitive</b> see {@link fr.cephb.lang.Endian fr.cephb.lang.Endian}
 *
 */
public class ByteWriter
extends DBUtils
{
private ByteArrayOutputStream out=new ByteArrayOutputStream();
/**
 * Default constructor
 */
public ByteWriter()
	{
	this(new ByteArrayOutputStream());
	}
/**
 * Default constructor with a given stream of bytes
 * @param byteArrayOutputStream the user output stream
 */
public ByteWriter(ByteArrayOutputStream byteArrayOutputStream)
	{
	this.out=byteArrayOutputStream;
	}

protected ByteArrayOutputStream getOut() {
	return out;
	}

protected void write(byte array[]) throws DatabaseException
	{
	try{
		getOut().write(array);
		}
	catch(IOException err)
		{
		throw new DatabaseException(err.getMessage());
		}
	}

/** get the bytes stored in the reader */
public byte[] getBytes()
	{
	return this.out.toByteArray();
	}

/** call getBytes and return a new DatabaseEntry */
public DatabaseEntry getEntry()
	{
	return new DatabaseEntry(getBytes());
	}


/** write a String and the '\0' character 
 * */
public ByteWriter writeCString(String s) throws DatabaseException
	{
	write(s.getBytes());
	getOut().write(0);//eof
	return this;
	}

/** write an unsigned int16
 * @see  fr.cephb.lang.Endian
 */
public ByteWriter writeUInt16(int value) throws DatabaseException
	{
	throw new UnsupportedOperationException("TODO");
	//return this;
	}

/** write an  int 16
 * @see  fr.cephb.lang.Endian
 */
public ByteWriter writeInt16(short value) throws DatabaseException
	{
	byte[] b = new byte[2];
	if(Endian.getDefault()==Endian.CEPH)
		{
	    b[0] = (byte) (value >>>  8);
	    b[1] = (byte) (value       );
		}
	else
		{
		 b[1] = (byte) (value >>>  8);
		 b[0] = (byte) (value       );	
		}
    write(b);
    return this;
	}

/** write an  int 8
 */
public ByteWriter writeInt8(byte value) throws DatabaseException
	{
	getOut().write(value);
	return this;
	}


/** write an  int 32
 * @see  fr.cephb.lang.Endian
 */
public ByteWriter writeInt32(int value) throws DatabaseException
	{
	byte[] b = new byte[4];
	if(Endian.getDefault()==Endian.CEPH)
		{
	    b[0] = (byte) (value >>> 24);
	    b[1] = (byte) (value >>> 16);
	    b[2] = (byte) (value >>>  8);
	    b[3] = (byte) (value       );
		}
	else
		{
		b[3] = (byte) (value >>> 24);
	    b[2] = (byte) (value >>> 16);
	    b[1] = (byte) (value >>>  8);
	    b[0] = (byte) (value       );	
		}
    write(b);
    return this;
	}

/** write an unsigned int 32
 * @see  fr.cephb.lang.Endian
 */
public ByteWriter writeUInt32(UInt32 value) throws DatabaseException
	{
	return writeUInt32(value.longValue());
	}

/**
 * OK, function validated on 2009-01-05
 * @param value
 * @return
 * @throws DatabaseException
 */
public ByteWriter writeUInt32(long value) throws DatabaseException
	{
	byte[] buf = new byte[4];
	if(Endian.getDefault()==Endian.CEPH)
		{
		buf[0] = (byte) (value >>> 24 & 0xFF);
	    buf[1] = (byte) (value >>> 16 & 0xFF);
	    buf[2] = (byte) (value >>> 8 & 0xFF);
	    buf[3] = (byte) (value >>> 0 & 0xFF);
		}
	else
		{
		buf[3] = (byte) (value >>> 24 & 0xFF);
	    buf[2] = (byte) (value >>> 16 & 0xFF);
	    buf[1] = (byte) (value >>> 8 & 0xFF);
	    buf[0] = (byte) (value >>> 0 & 0xFF);
		}
    write(buf);
    return this;
	}

/** write an unsigned int 64
 * @see  fr.cephb.lang.Endian
 */
public ByteWriter writeInt64(long value) throws DatabaseException
	{
	byte[] b = new byte[8];
	
	if(Endian.getDefault()==Endian.CEPH)
		{
		b[0] = (byte) (value >>> 56);
		b[1] = (byte) (value >>> 48);
		b[2] = (byte) (value >>> 40);
		b[3] = (byte) (value >>> 32);
		b[4] = (byte) (value >>> 24);
		b[5] = (byte) (value >>> 16);
		b[6] = (byte) (value >>>  8);
		b[7] = (byte) (value       );
		}
	else
		{
		b[7] = (byte) (value >>> 56);
		b[6] = (byte) (value >>> 48);
		b[5] = (byte) (value >>> 40);
		b[4] = (byte) (value >>> 32);
		b[3] = (byte) (value >>> 24);
		b[2] = (byte) (value >>> 16);
		b[1] = (byte) (value >>>  8);
		b[0] = (byte) (value       );
		}
	
	write(b);
	return this;
}
/** write a float 32
 */
public ByteWriter writeFloat32(float value)
	{
	throw new UnsupportedOperationException("writeFloat32");
	}
/** write a float 64
 */
public ByteWriter writeFloat64(double value)
	{
	throw new UnsupportedOperationException("writeFloat64");
	}

}
